from tkinter import *
import tkinter as tk
from tkinter.filedialog import askopenfilename
from PIL import Image,ImageTk
import cv2
import numpy as np
import keras

# 预加载模型
unet = keras.models.load_model("unet.h5")
cnn = keras.models.load_model("cnn.h5")

class Window:
    def __init__(self,win,ww,wh):
        self.win = win
        self.ww = ww
        self.wh = wh
        self.win.geometry("%dx%d+%d+%d" % (ww, wh, 200, 50))  # 界面启动时的初始位置
        self.win.title("车牌识别软件")
        self.img_src_path = None

        self.label_src = Label(self.win, text='原图:', font=('微软雅黑', 13)).place(x=0, y=0)
        self.label_lic1 = Label(self.win, text='车牌区域1:', font=('微软雅黑', 13)).place(x=615, y=0)
        self.label_pred1 = Label(self.win, text='识别结果1:', font=('微软雅黑', 13)).place(x=615, y=85)
        self.label_lic2 = Label(self.win, text='车牌区域2:', font=('微软雅黑', 13)).place(x=615, y=180)
        self.label_pred2 = Label(self.win, text='识别结果2:', font=('微软雅黑', 13)).place(x=615, y=265)
        self.label_lic3 = Label(self.win, text='车牌区域3:', font=('微软雅黑', 13)).place(x=615, y=360)
        self.label_pred3 = Label(self.win, text='识别结果3:', font=('微软雅黑', 13)).place(x=615, y=445)

        self.can_src = Canvas(self.win, width=512, height=512, bg='white', relief='solid', borderwidth=1)  # 原图画布
        self.can_src.place(x=50, y=0)
        self.can_lic1 = Canvas(self.win, width=245, height=85, bg='white', relief='solid', borderwidth=1)  # 车牌区域1画布
        self.can_lic1.place(x=710, y=0)
        self.can_pred1 = Canvas(self.win, width=245, height=65, bg='white', relief='solid', borderwidth=1)  # 车牌识别1画布
        self.can_pred1.place(x=710, y=90)
        self.can_lic2 = Canvas(self.win, width=245, height=85, bg='white', relief='solid', borderwidth=1)  # 车牌区域2画布
        self.can_lic2.place(x=710, y=175)
        self.can_pred2 = Canvas(self.win, width=245, height=65, bg='white', relief='solid', borderwidth=1)  # 车牌识别2画布
        self.can_pred2.place(x=710, y=265)
        self.can_lic3 = Canvas(self.win, width=245, height=85, bg='white', relief='solid', borderwidth=1)  # 车牌区域3画布
        self.can_lic3.place(x=710, y=350)
        self.can_pred3 = Canvas(self.win, width=245, height=65, bg='white', relief='solid', borderwidth=1)  # 车牌识别3画布
        self.can_pred3.place(x=710, y=440)

        self.button1 = Button(self.win, text='选择文件', width=10, height=1,command=self.openFile)  # 选择文件按钮
        self.button1.place(x=680, y=wh - 30)
        self.button2 = Button(self.win, text='识别车牌', width=10, height=1,command=self.detect_panel)  # 识别车牌按钮
        self.button2.place(x=780, y=wh - 30)
        self.button3 = Button(self.win, text='清空所有', width=10, height=1)  # 清空所有按钮
        self.button3.place(x=880, y=wh - 30)

    def openFile(self):
        self.v = tk.StringVar()
        self.v.set(askopenfilename())
        # 通过Entry编辑项模拟选取图片，并传递路径的过程
        self.img_src_path = tk.Entry(self.win, state="readonly", text=self.v).get()

        image = Image.open(self.img_src_path)
        image = image.resize((512, 512))
        self.img_tk = ImageTk.PhotoImage(image)

        self.can_src.create_image(256, 256, image=self.img_tk, anchor="center")

    # -------------------------加载车牌图像，检测车牌区域-----------------------------
    def detect_panel(self):
        if self.img_src_path == "":
            return
        image = cv2.imdecode(np.fromfile(self.img_src_path, dtype=np.uint8), -1)
        if image.shape != (512, 512, 3):
            image = cv2.resize(image, dsize=(512, 512), interpolation=cv2.INTER_AREA)[:, :, :3]
        image = image.reshape(1, 512, 512, 3)

        img_mask = unet.predict(image)
        img_mask = img_mask.reshape(512, 512, 3)
        img_mask = img_mask / np.max(img_mask) * 255  # 将像素值控制到0- 255 之间
        img_mask[:, :, 2] = img_mask[:, :, 1] = img_mask[:, :, 0]  # 三个通道保持相同
        img_mask = np.array(img_mask, dtype=np.uint8)

        try:
            # opencv3.0
            contours, hierarchy = cv2.findContours(img_mask[:, :, 0], cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        except:
            # opencv2.0
            ret, contours, hierarchy = cv2.findContours(img_mask[:, :, 0], cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        image = image.reshape(512, 512, 3)
        for cont in contours:
            x, y, w, h = cv2.boundingRect(cont)
            x0, y0 = x, y
            x1, y1 = x, y + h
            x2, y2 = x + w, y
            x3, y3 = x + w, y + h

            d0, d1, d2, d3 = np.inf, np.inf, np.inf, np.inf
            l0, l1, l2, l3 = (x0, y0), (x1, y1), (x2, y2), (x3, y3)
            for item in cont:
                (current_x, current_y) = item[0]
                dis0 = (current_x - x0) ** 2 + (current_y - y0) ** 2
                dis1 = (current_x - x1) ** 2 + (current_y - y1) ** 2
                dis2 = (current_x - x2) ** 2 + (current_y - y2) ** 2
                dis3 = (current_x - x3) ** 2 + (current_y - y3) ** 2
                if dis0 < d0:
                    d0 = dis0
                    l0 = (current_x, current_y)
                if dis1 < d1:
                    d1 = dis1
                    l1 = (current_x, current_y)
                if dis2 < d2:
                    d2 = dis2
                    l2 = (current_x, current_y)
                if dis3 < d3:
                    d3 = dis3
                    l3 = (current_x, current_y)
            image_copy = image.copy()
            cv2.line(image_copy, l0, l2, color=(0, 255, 0), thickness=2)
            cv2.line(image_copy, l2, l3, color=(0, 255, 0), thickness=2)
            cv2.line(image_copy, l3, l1, color=(0, 255, 0), thickness=2)
            cv2.line(image_copy, l1, l0, color=(0, 255, 0), thickness=2)

            # 仿射变换车牌
            pts1 = np.float32([l0, l2, l1, l3])
            # 车牌的标准大小为240x80
            pst2 = np.float32([
                [0, 0],
                [240, 0],
                [0, 80],
                [240, 80]
            ])

            # 仿射变化的核心
            M = cv2.getPerspectiveTransform(pts1, pst2)
            # 将梯形拉伸成了矩形
            dic_img = cv2.warpPerspective(image, M, (240, 80))
            # dic_img = Image.fromarray(dic_img[:, :, ::-1])
            # self.dic_img_tk = ImageTk.PhotoImage(dic_img)
            # self.can_lic1.delete("all")
            # self.can_lic1.create_image(5, 5, image=self.dic_img_tk, anchor="nw")
            dic_img = dic_img.reshape(1,80,240,3)
            Lic_pred = cnn_predict(cnn, [dic_img])
            print(Lic_pred)

        # 将 cv2下的image转化为PIL中的对象
        image_copy = Image.fromarray(image_copy[:, :, ::-1])
        self.img_tk = ImageTk.PhotoImage(image_copy)
        self.can_src.delete("all")
        self.can_src.create_image(256, 256, image=self.img_tk, anchor="center")

def cnn_predict(cnn, Lic_img):
    characters = ["京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑", "苏", "浙", "皖", "闽", "赣", "鲁",
                  "豫",
                  "鄂", "湘", "粤", "桂", "琼", "川", "贵", "云", "藏", "陕", "甘", "青", "宁", "新", "0", "1", "2",
                  "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M",
                  "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
    Lic_pred = []
    for lic in Lic_img:
        lic_pred = cnn.predict(lic.reshape(1, 80, 240, 3))  # 预测形状应为(1,80,240,3)
        lic_pred = np.array(lic_pred).reshape(7, 65)  # 列表转为ndarray，形状为(7,65)
        if len(lic_pred[lic_pred >= 0.8]) >= 4:  # 统计其中预测概率值大于80%以上的个数，大于等于4个以上认为识别率高，识别成功
            chars = ''
            for arg in np.argmax(lic_pred, axis=1):  # 取每行中概率值最大的arg,将其转为字符
                chars += characters[arg]
            chars = chars[0:2] + '·' + chars[2:]
            Lic_pred.append((lic, chars))  # 将车牌和识别结果一并存入Lic_pred
    return Lic_pred

if __name__ == '__main__':
    window = tk.Tk()
    Window(window,1000,600)
    window.mainloop()



